
#include <bsp_usart.h>
#include <rtthread.h>
#include <ov5640_xclk.h>
#include <ov5640_i2c.h>
#include <ov5640_camera.h>
#include <ov5640_port.h>

extern void Error_Handler(void);

static DMA_HandleTypeDef hdma_tx;
static DMA_HandleTypeDef hdma_rx;

/**
 * @brief  DeInitializes the Global MSP.
 * @retval None
 */
void HAL_UART_MspDeInit(UART_HandleTypeDef *huart)
{
    /*##-1- Reset peripherals ##################################################*/
    DEBUG_UART_FORCE_RESET();
    DEBUG_UART_RELEASE_RESET();

    /*##-2- Disable peripherals and GPIO Clocks #################################*/
    /* Configure USARTx Tx as alternate function  */
    HAL_GPIO_DeInit(DEBUG_UART_PIN_GROUP, DEBUG_UART_TX_PIN);
    /* Configure USARTx Rx as alternate function  */
    HAL_GPIO_DeInit(DEBUG_UART_PIN_GROUP, DEBUG_UART_RX_PIN);

    /*##-3- Disable the DMA #####################################################*/
    /* De-Initialize the DMA channel associated to reception process */
    if (huart->hdmarx != 0)
    {
        HAL_DMA_DeInit(huart->hdmarx);
    }
    /* De-Initialize the DMA channel associated to transmission process */
    if (huart->hdmatx != 0)
    {
        HAL_DMA_DeInit(huart->hdmatx);
    }

    /*##-4- Disable the NVIC  ###########################################*/
    HAL_NVIC_DisableIRQ(DEBUG_UART_IRQn);
    HAL_NVIC_DisableIRQ(DEBUG_UART_DMA_TX_IRQn);
    HAL_NVIC_DisableIRQ(DEBUG_UART_DMA_RX_IRQn);
}

/**
 * @brief UART MSP Initialization
 *        This function configures the hardware resources used in this example:
 *           - Peripheral's clock enable
 *           - Peripheral's GPIO Configuration
 *           - DMA configuration for transmission request by peripheral
 *           - NVIC configuration for DMA interrupt request enable
 * @param huart: UART handle pointer
 * @retval None
 */
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    if (huart->Instance == DEBUG_UART)
    {
        /*##-1- Enable peripherals Clocks #################################*/
        /* Enable GPIO TX/RX clock */
        DEBUG_UART_GPIO_RCC_EN;

        /* Enable USARTx clock */
        DEBUG_UART_RCC_EN;

        /* Enable DMA clock */
        DEBUG_UART_DMA_CLK_ENABLE;

        /*##-2- Configure peripheral GPIO ##########################################*/
        /* UART TX GPIO pin configuration  */
        GPIO_InitStruct.Pin = DEBUG_UART_TX_PIN;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
        GPIO_InitStruct.Alternate = DEBUG_UART_TX_RX_PIN_AF;

        HAL_GPIO_Init(DEBUG_UART_PIN_GROUP, &GPIO_InitStruct);

        /* UART RX GPIO pin configuration  */
        GPIO_InitStruct.Pin = DEBUG_UART_RX_PIN;
        GPIO_InitStruct.Alternate = DEBUG_UART_TX_RX_PIN_AF;

        HAL_GPIO_Init(DEBUG_UART_PIN_GROUP, &GPIO_InitStruct);

        // /*##-3- Configure the DMA ##################################################*/
        // /* Configure the DMA handler for Transmission process */
        RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
        PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_UART7;
        PeriphClkInitStruct.Usart234578ClockSelection = RCC_USART234578CLKSOURCE_D2PCLK1;
        if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
        {
            Error_Handler();
        }
        hdma_tx.Instance = DEBUG_UART_TX_DMA_STREAM;
        hdma_tx.Init.Request = DEBUG_UART_TX_DMA_REQUEST;
        hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
        hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_tx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_tx.Init.Mode = DMA_NORMAL;
        hdma_tx.Init.Priority = DMA_PRIORITY_HIGH;
        hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
        hdma_tx.Init.MemBurst = DMA_MBURST_INC4;
        hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4;

        if (HAL_DMA_Init(&hdma_tx) != HAL_OK)
        {
            Error_Handler();
        }

        __HAL_LINKDMA(huart, hdmatx, hdma_tx);

        /* Configure the DMA handler for reception process */
        hdma_rx.Instance = DEBUG_UART_RX_DMA_STREAM;
        hdma_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_rx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_rx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_rx.Init.Mode = DMA_NORMAL;
        hdma_rx.Init.Priority = DMA_PRIORITY_HIGH;
        hdma_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        hdma_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
        hdma_rx.Init.MemBurst = DMA_MBURST_INC4;
        hdma_rx.Init.PeriphBurst = DMA_PBURST_INC4;
        hdma_rx.Init.Request = DEBUG_UART_RX_DMA_REQUEST;

        HAL_DMA_Init(&hdma_rx);

        /* Associate the initialized DMA handle to the the UART handle */
        __HAL_LINKDMA(huart, hdmarx, hdma_rx);

        // /* DMA1_Stream1_IRQn interrupt configuration */
        HAL_NVIC_SetPriority(DEBUG_UART_DMA_TX_IRQn, 0, 1);
        HAL_NVIC_EnableIRQ(DEBUG_UART_DMA_TX_IRQn);

        HAL_NVIC_SetPriority(DEBUG_UART_DMA_RX_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(DEBUG_UART_DMA_RX_IRQn);

        // /* DMAMUX1_OVR_IRQn interrupt configuration */
        // HAL_NVIC_SetPriority(DMAMUX2_OVR_IRQn, 0, 0);
        // HAL_NVIC_EnableIRQ(DMAMUX2_OVR_IRQn);

        /*##-4- Configure the NVIC for UART #########################################*/

        /* NVIC for USART, to catch the TX complete */
        HAL_NVIC_SetPriority(DEBUG_UART_IRQn, 0, 1);
        HAL_NVIC_EnableIRQ(DEBUG_UART_IRQn);

        __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); /* idle interrupt */
    }

    if (huart->Instance == DOWNLOAD_UART)
    {
        /*##-1- Enable peripherals Clocks #################################*/
        /* Enable GPIO TX/RX clock */
        DOWNLOAD_UART_GPIO_RCC_EN;

        /* Enable USARTx clock */
        DOWNLOAD_UART_RCC_EN;

        /*##-2- Configure peripheral GPIO ##########################################*/
        /* UART TX GPIO pin configuration  */
        GPIO_InitStruct.Pin = DOWNLOAD_UART_TX_PIN;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
        GPIO_InitStruct.Alternate = DOWNLOAD_UART_TX_RX_PIN_AF;

        HAL_GPIO_Init(DOWNLOAD_UART_PIN_GROUP, &GPIO_InitStruct);

        /* UART RX GPIO pin configuration  */
        GPIO_InitStruct.Pin = DOWNLOAD_UART_RX_PIN;
        GPIO_InitStruct.Alternate = DOWNLOAD_UART_TX_RX_PIN_AF;

        HAL_GPIO_Init(DOWNLOAD_UART_PIN_GROUP, &GPIO_InitStruct);

        // /*##-3- Configure the DMA ##################################################*/
        // /* Configure the DMA handler for Transmission process */
        RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
        PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART1;
        PeriphClkInitStruct.Usart234578ClockSelection = RCC_USART1CLKSOURCE_D2PCLK2;
        if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
        {
            Error_Handler();
        }
        hdma_tx.Instance = DOWNLOAD_UART_TX_DMA_STREAM;
        hdma_tx.Init.Request = DOWNLOAD_UART_TX_DMA_REQUEST;
        hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
        hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_tx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
        hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
        hdma_tx.Init.Mode = DMA_NORMAL;
        hdma_tx.Init.Priority = DMA_PRIORITY_HIGH;
        hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
        hdma_tx.Init.MemBurst = DMA_MBURST_INC4;
        hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4;

        if (HAL_DMA_Init(&hdma_tx) != HAL_OK)
        {
            Error_Handler();
        }

        __HAL_LINKDMA(huart, hdmatx, hdma_tx);

        // /* DMA1_Stream1_IRQn interrupt configuration */
        HAL_NVIC_SetPriority(DEBUG_UART_DMA_TX_IRQn, 0, 1);
        HAL_NVIC_EnableIRQ(DEBUG_UART_DMA_TX_IRQn);

        // /* DMAMUX1_OVR_IRQn interrupt configuration */
        // HAL_NVIC_SetPriority(DMAMUX1_OVR_IRQn, 0, 0);
        // HAL_NVIC_EnableIRQ(DMAMUX1_OVR_IRQn);

        /*##-4- Configure the NVIC for UART #########################################*/

        /* NVIC for USART, to catch the TX complete */
        HAL_NVIC_SetPriority(DOWNLOAD_UART_IRQn, 0, 1);
        HAL_NVIC_EnableIRQ(DOWNLOAD_UART_IRQn);
    }
}

void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /** Supply configuration update enable
     */
    HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);

    /** Configure the main internal regulator output voltage
     */
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

    while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY))
    {
    }

    /** Initializes the RCC Oscillators according to the specified parameters
     * in the RCC_OscInitTypeDef structure.
     */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 8;
    RCC_OscInitStruct.PLL.PLLN = 480;
    RCC_OscInitStruct.PLL.PLLP = 2;
    RCC_OscInitStruct.PLL.PLLQ = 2;
    RCC_OscInitStruct.PLL.PLLR = 2;
    RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_0;
    RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    RCC_OscInitStruct.PLL.PLLFRACN = 0;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }

    /** Initializes the CPU, AHB and APB buses clocks
     */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1 | RCC_CLOCKTYPE_D1PCLK1;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
    RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
    {
        Error_Handler();
    }
}

/* cortex-m7 has MPU region */
void cortex_m7_mpu_init(void)
{
    /* 简单的说就是内存保护、外设保护和代码访问保护 */
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* Disable the MPU */
    HAL_MPU_Disable();

    /** Initializes and configures the Region and the memory to be protected
     */
    MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    MPU_InitStruct.Number = MPU_REGION_NUMBER0;
    MPU_InitStruct.BaseAddress = 0x0;
    MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
    MPU_InitStruct.SubRegionDisable = 0x87;
    MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
    MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
    MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
    MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
    MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
    MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /* Enable the MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/**
 * @brief  CPU L1-Cache enable.
 * @param  None
 * @retval None
 */
void cortex_m7_L1_cpu_cache_enable(void)
{
    /* Enable I-Cache */
    SCB_EnableICache();

    /* Enable D-Cache */
    SCB_EnableDCache();
}

/**
 * @brief This function handles DMA1 stream0 global interrupt.
 */
void DEBUG_UART_DMA_RX_IRQHandler(void)
{
    /* USER CODE BEGIN DMA1_Stream0_IRQn 0 */
    rt_interrupt_enter();
    /* USER CODE END DMA1_Stream0_IRQn 0 */
    HAL_DMA_IRQHandler(&hdma_rx);
    /* USER CODE BEGIN DMA1_Stream0_IRQn 1 */
    rt_interrupt_leave();
    /* USER CODE END DMA1_Stream0_IRQn 1 */
}

/**
 * @brief This function handles DMA1 stream1 global interrupt.
 */
void DEBUG_UART_DMA_TX_IRQHandler(void)
{
    /* USER CODE BEGIN DMA1_Stream1_IRQn 0 */
    rt_interrupt_enter();
    /* USER CODE END DMA1_Stream1_IRQn 0 */
    HAL_DMA_IRQHandler(&hdma_tx);
    /* USER CODE BEGIN DMA1_Stream1_IRQn 1 */
    rt_interrupt_leave();
    /* USER CODE END DMA1_Stream1_IRQn 1 */
}

void DMAMUX2_OVR_IRQHandler(void)
{
    /* USER CODE BEGIN DMAMUX1_OVR_IRQn 0 */

    /* USER CODE END DMAMUX1_OVR_IRQn 0 */

    /* USER CODE BEGIN DMAMUX1_OVR_IRQn 1 */

    /* USER CODE END DMAMUX1_OVR_IRQn 1 */
}

